home *** CD-ROM | disk | FTP | other *** search
/ ETO Development Tools 2 / ETO Development Tools 2.iso / Tools - Objects / MacApp / MacApp CD Release / MacApp 2.0.1 (Many Libraries) / Libraries / UMacApp.TDocument.p < prev    next >
Encoding:
Text File  |  1990-10-25  |  43.0 KB  |  1,735 lines  |  [TEXT/MPS ]

  1. {$P}
  2. {[a-,body+,h-,o=100,r+,rec+,t=4,u+,#+,j=20/57/1$,n-]}
  3. { UMacApp.TDocument.p }
  4. { Copyright © 1984-1990 by Apple Computer Inc.    All rights reserved. }
  5.  
  6. {--------------------------------------------------------------------------------------------------}
  7. {$S MAWriteFile}
  8.  
  9. PROCEDURE TSaveDocCommand.DoIt;
  10.  
  11.     BEGIN
  12.     fChangedDocument.Save(fCmdNumber,
  13.     {askForFilename:} NOT fChangedDocument.fSaveExists | (fCmdNumber <> cSave),
  14.     {makingCopy:} fCmdNumber = cSaveCopy);
  15.     END;
  16.  
  17. {--------------------------------------------------------------------------------------------------}
  18. {$S MASelCommand}
  19.  
  20. PROCEDURE TSaveDocCommand.ISaveDocCommand(itsCmdNumber: CmdNumber;
  21.                                           itsDocument: TDocument);
  22.  
  23.     BEGIN
  24.     INoChangesCommand(itsCmdNumber, itsDocument, NIL, NIL);
  25.     END;
  26.  
  27. {--------------------------------------------------------------------------------------------------}
  28. {$S MAFields}
  29.  
  30. PROCEDURE TSaveDocCommand.Fields(PROCEDURE DoToField(fieldName: Str255;
  31.                                                      fieldAddr: Ptr;
  32.                                                      fieldType: INTEGER)); OVERRIDE;
  33.  
  34.     BEGIN
  35.     DoToField('TSaveDocCommand', NIL, bClass);
  36.     INHERITED Fields(DoToField);
  37.     END;
  38.  
  39. {--------------------------------------------------------------------------------------------------}
  40. {$S MAReadFile}
  41.  
  42. PROCEDURE TRevertDocCommand.DoIt;
  43.  
  44.     VAR
  45.         name:                Str255;
  46.         fi:                 FailInfo;
  47.  
  48.     PROCEDURE HdlRevertCmd(error: OSErr;
  49.                            message: LONGINT);
  50.  
  51.         BEGIN
  52.         fChangedDocument.ShowReverted;                    { make sure screen is updated }
  53.         END;
  54.  
  55.     BEGIN
  56.     name := fChangedDocument.fTitle^^;
  57.     ParamText(name, '', '', '');
  58.     IF MacAppAlert(phRevert, NIL) = kYesButton THEN     {!!! This should be programatically
  59.                                                          defeatable }
  60.         BEGIN
  61.         CatchFailures(fi, HdlRevertCmd);
  62.         fChangedDocument.Revert;
  63.         Success(fi);
  64.         fChangedDocument.ShowReverted;
  65.         END;
  66.     END;
  67.  
  68. {--------------------------------------------------------------------------------------------------}
  69. {$S MASelCommand}
  70.  
  71. PROCEDURE TRevertDocCommand.IRevertDocCommand(itsCmdNumber: CmdNumber;
  72.                                               itsDocument: TDocument);
  73.  
  74.     BEGIN
  75.     INoChangesCommand(itsCmdNumber, itsDocument, NIL, NIL);
  76.     END;
  77.  
  78. {--------------------------------------------------------------------------------------------------}
  79. {$S MAFields}
  80.  
  81. PROCEDURE TRevertDocCommand.Fields(PROCEDURE DoToField(fieldName: Str255;
  82.                                                        fieldAddr: Ptr;
  83.                                                        fieldType: INTEGER)); OVERRIDE;
  84.  
  85.     BEGIN
  86.     DoToField('TRevertDocCommand', NIL, bClass);
  87.     INHERITED Fields(DoToField);
  88.     END;
  89.  
  90. {--------------------------------------------------------------------------------------------------}
  91. {$S MAOpen}
  92.  
  93. PROCEDURE TDocument.IDocument(itsFileType, itsCreator: OSType;
  94.                               usesDataFork, usesRsrcFork: BOOLEAN;
  95.                               keepsDataOpen, keepsRsrcOpen: BOOLEAN);
  96.  
  97.     VAR
  98.         fi:                 FailInfo;
  99.  
  100.     PROCEDURE HdlIDocument(error: OSErr;
  101.                            message: LONGINT);
  102.  
  103.         BEGIN
  104.         Free;
  105.         END;
  106.  
  107.     BEGIN
  108.     fWindowList := NIL;
  109.     fViewList := NIL;
  110.     fPrintInfo := NIL;
  111.     fDocPrintHandler := NIL;
  112.  
  113.     fDataRefnum := kNoFileRefnum;
  114.     fRsrcRefnum := kNoFileRefnum;
  115.  
  116.     fTitle := NIL;
  117.     fPrintInfo := NIL;
  118.     fSavePrintInfo := FALSE;
  119.     fSharePrintInfo := TRUE;
  120.  
  121.     fVolRefNum := 0;
  122.     fReopenAlert := TRUE;
  123.     fSaveExists := FALSE;
  124.     fCommitOnSave := TRUE;
  125.     fModDate := 0;
  126.  
  127.     fFileType := itsFileType;
  128.     fCreator := itsCreator;
  129.  
  130.     fUsesDataFork := usesDataFork;
  131.     fUsesRsrcFork := usesRsrcFork;
  132.     fDataOpen := keepsDataOpen;
  133.     fRsrcOpen := keepsRsrcOpen;
  134.  
  135.     {$IFC qDebug}
  136.     IF fDataOpen & (NOT fUsesDataFork) THEN
  137.         BEGIN
  138.         Writeln('In TDocument.IDocument:  fDataOpen AND NOT fUsesDataFork;');
  139.         Writeln('In TDocument.IDocument:  fUsesDataFork := TRUE;');
  140.         fUsesDataFork := TRUE;
  141.         END;
  142.     IF fRsrcOpen & (NOT fUsesRsrcFork) THEN
  143.         BEGIN
  144.         Writeln('In TDocument.IDocument:  fRsrcOpen AND NOT fUsesRsrcFork;');
  145.         Writeln('In TDocument.IDocument:  fUsesRsrcFork := TRUE;');
  146.         fUsesRsrcFork := TRUE;
  147.         END;
  148.     {$ENDC}
  149.  
  150.     IF keepsDataOpen | keepsRsrcOpen THEN
  151.         fSaveInPlace := sipNever
  152.     ELSE
  153.         fSaveInPlace := sipAskUser;
  154.  
  155.     fDataPerm := fsRdPerm;
  156.     fRsrcPerm := fsRdPerm;
  157.  
  158.     IEvtHandler(gApplication);
  159.  
  160.     CatchFailures(fi, HdlIDocument);
  161.  
  162.     fChangeCount := 0;
  163.  
  164.     fTitle := NewString('');
  165.     FailNil(fTitle);
  166.  
  167.     fWindowList := NewList;
  168.     {$IFC qDebug}
  169.     fWindowList.SetEltType('TWindow');
  170.     {$ENDC}
  171.  
  172.     fViewList := NewList;
  173.     {$IFC qDebug}
  174.     fViewList.SetEltType('TView');
  175.     {$ENDC}
  176.  
  177.     Success(fi);
  178.     END;
  179.  
  180. {--------------------------------------------------------------------------------------------------}
  181. {$S MAClose}
  182.  
  183. PROCEDURE TDocument.Free; OVERRIDE;
  184.  
  185.     BEGIN
  186.     gApplication.DeleteDocument(SELF);
  187.  
  188.     FreeFile;
  189.  
  190.     fWindowList := FreeListIfObject(fWindowList);
  191.     fViewList := FreeListIfObject(fViewList);
  192.  
  193.     IF fSharePrintInfo THEN
  194.         fPrintInfo := DisposeIfHandle(fPrintInfo);
  195.     fPrintInfo := NIL;    { Always drop my reference }
  196.  
  197.     Handle(fTitle) := DisposeIfHandle(fTitle);
  198.  
  199.     INHERITED Free;
  200.     END;
  201.  
  202. {--------------------------------------------------------------------------------------------------}
  203. {$S MAOpen}
  204.  
  205. PROCEDURE TDocument.AddView(aView: TView);
  206.  
  207.     BEGIN
  208.     { Protect against double installation and keep in synch with window list }
  209.  
  210.     IF (fViewList <> NIL) & (fViewList.GetSameItemNo(aView) = 0) THEN
  211.         fViewList.Insert(aView);
  212.  
  213.     IF (fWindowList <> NIL) & member(aView, TWindow) THEN
  214.         IF fWindowList.GetSameItemNo(aView) = 0 THEN
  215.             fWindowList.Insert(aView);
  216.     END;
  217.  
  218. {--------------------------------------------------------------------------------------------------}
  219. {$S MAWriteFile}
  220.  
  221. PROCEDURE TDocument.AboutToSave(itsCmd: CmdNumber;
  222.                                 VAR newName: Str255;
  223.                                 VAR newVolRefnum: INTEGER;
  224.                                 VAR makingCopy: BOOLEAN);
  225.  
  226.     BEGIN
  227.     END;
  228.  
  229. {--------------------------------------------------------------------------------------------------}
  230. {$S MAOpen}
  231.  
  232. PROCEDURE TDocument.AddWindow(aWindow: TWindow);
  233.  
  234.     BEGIN
  235.     { Protect against double installation and keep in synch with window list }
  236.     IF (fWindowList <> NIL) & (fWindowList.GetSameItemNo(aWindow) = 0) THEN { doesn't already exist in
  237.                                                                            list }
  238.         fWindowList.Insert(aWindow);
  239.  
  240.     IF (fViewList <> NIL) & (fViewList.GetSameItemNo(aWindow) = 0) THEN { ??? should we only have one
  241.                                                                        list now… maybe created on
  242.                                                                        demand? (post 2.0) }
  243.         fViewList.Insert(aWindow);
  244.     END;
  245.  
  246. {--------------------------------------------------------------------------------------------------}
  247. {$S MAFile}
  248.  
  249. PROCEDURE TDocument.CheckDiskFile(rsrcId, rsrcIndex: INTEGER;
  250.                                   reverting: BOOLEAN);
  251.  
  252.     VAR
  253.         err:                OSErr;
  254.         name:                Str255;
  255.         s:                    Str255;
  256.  
  257.     BEGIN
  258.     err := DiskFileChanged(reverting);                    {don't care about the file type if saving}
  259.     IF err = errFileChanged THEN
  260.         BEGIN
  261.         name := fTitle^^;
  262.         GetIndString(s, rsrcId, rsrcIndex);
  263.         ParamText(name, s, '', '');
  264.         IF MacAppAlert(phFileChanged, NIL) = cancel THEN {!!! This should be programatically
  265.                                                           defeatable }
  266.             Failure(noErr, msgCancelled);
  267.         END
  268.         {if reverting then we signal an error}
  269.     ELSE IF (err <> noErr) & reverting THEN
  270.         Failure(err, 0);
  271.     END;
  272.  
  273. {--------------------------------------------------------------------------------------------------}
  274. {$S MAClose}
  275.  
  276. PROCEDURE TDocument.CloseView(aView: TView);
  277.  
  278.     FUNCTION OpenWindowCount: ArrayIndex;
  279.     
  280.         VAR
  281.             openDocWindows: integer;
  282.     
  283.         PROCEDURE CountOpenWindows(aWindow: TWindow);
  284.     
  285.             BEGIN
  286.             IF aWindow.IsShown THEN
  287.                 openDocWindows := openDocWindows + 1;
  288.             END;
  289.  
  290.         BEGIN
  291.         { See how many open windows this document has }
  292.         openDocWindows := 0;
  293.         ForAllWindowsDo(CountOpenWindows);
  294.         OpenWindowCount := openDocWindows;
  295.         END;
  296.  
  297.     BEGIN
  298.     IF aView.fDocument = SELF THEN                             { free window }
  299.         BEGIN
  300.         { !!! Yuch!, of course we should be able to ASK any view if it would like to close
  301.         its associated document and maybe even what doc it would like to close.
  302.         Some other time… }
  303.         IF (Member(aView, TWindow) & (TWindow(aView).fClosesDocument) | (OpenWindowCount <= 1)) THEN
  304.             Close { The view will be closed and freed as a side effect }
  305.         ELSE
  306.             aView.Close;
  307.         END;
  308.     END;
  309.  
  310.  
  311. {--------------------------------------------------------------------------------------------------}
  312. {$S MAClose}
  313.  
  314. PROCEDURE TDocument.Close;
  315.  
  316.     VAR
  317.         lastCommand:        TCommand;
  318.         poseResult:            INTEGER;
  319.         changeCount:        LONGINT;
  320.  
  321.     {Must never be called for a document related to a view in the Clipboard.
  322.         Why is this???}
  323.  
  324.     PROCEDURE CloseAWindow(aWindow: TWindow);
  325.  
  326.         BEGIN
  327.         aWindow.Close
  328.         END;
  329.  
  330.     BEGIN
  331.     {$IFC qDebug}
  332.     IF gClipWindow.fDocument = SELF THEN
  333.         ProgramBreak('Attempt to close clipboard document');
  334.     {$ENDC}
  335.  
  336.     changeCount := GetChangeCount;
  337.     IF changeCount <> 0 THEN
  338.         BEGIN
  339.         poseResult := PoseSaveDialog;
  340.         CASE poseResult OF
  341.             cancel:
  342.                 Failure(noErr, msgCancelled);
  343.         END;
  344.         END;
  345.  
  346.     lastCommand := GetLastCommand;
  347.     IF (lastCommand <> NIL) & (lastCommand.fChangedDocument = SELF) THEN
  348.         CommitLastCommand;
  349.  
  350.     IF changeCount <> 0 THEN
  351.         BEGIN
  352.         CASE poseResult OF
  353.             kYesButton:
  354.             {Will fail if unable to save}
  355.                 Save(cClose,                            {askForFilename:} NOT fSaveExists,
  356.                      kSwitchToTarget);
  357.             kNoButton:
  358.                 Abandon;
  359.             END;
  360.         END;
  361.  
  362.     ForAllWindowsDo(CloseAWindow);
  363.  
  364.     Free;
  365.     END;
  366.  
  367. {--------------------------------------------------------------------------------------------------}
  368. {$S MAClose}
  369.  
  370. PROCEDURE TDocument.DeleteView(viewToDelete: TView);
  371.  
  372.     BEGIN
  373.     IF (fViewList <> NIL) THEN
  374.         fViewList.Delete(viewToDelete);
  375.  
  376.     IF (fWindowList <> NIL) THEN
  377.         fWindowList.Delete(viewToDelete);                { Make sure the lists are in synch. ???
  378.                                                          should we only have one list now (post
  379.                                                          2.0) }
  380.     END;
  381.  
  382. {--------------------------------------------------------------------------------------------------}
  383. {$S MAClose}
  384.  
  385. PROCEDURE TDocument.DeleteWindow(windowToDelete: TWindow);
  386.  
  387.     BEGIN
  388.     IF (fWindowList <> NIL) THEN
  389.         fWindowList.Delete(windowToDelete);
  390.  
  391.     IF (fViewList <> NIL) THEN
  392.         fViewList.Delete(windowToDelete);                { Make sure the lists are in synch. ???
  393.                                                          should we only have one list now (post
  394.                                                          2.0) }
  395.     END;
  396.  
  397. {--------------------------------------------------------------------------------------------------}
  398. {$S MAFile}
  399.  
  400. FUNCTION TDocument.DiskFileChanged(checkType: BOOLEAN): OSErr;
  401.  
  402.     VAR
  403.         pb:                 HParamBlockRec;
  404.         err:                OSErr;
  405.         fi:                 FailInfo;
  406.  
  407.     PROCEDURE HdlGetFileInfo(error: OSErr;
  408.                              message: LONGINT);
  409.  
  410.         BEGIN
  411.         Free;
  412.         END;
  413.  
  414.     BEGIN
  415.     IF fSaveExists THEN
  416.         BEGIN
  417.         CatchFailures(fi, HdlGetFileInfo);
  418.         LockHandleHigh(Handle(fTitle));
  419.         {$Push} {$H-}
  420.         err := GetFileInfo(fTitle^^, fVolRefNum, pb);
  421.         {$Pop}
  422.         HUnLock(Handle(fTitle));
  423.         Success(fi);
  424.         IF (err = noErr) & checkType & (pb.ioFlFndrInfo.fdType <> fFileType) THEN
  425.             err := errFTypeChanged
  426.         ELSE IF pb.ioFlMdDat <> fModDate THEN
  427.             err := errFileChanged;
  428.         DiskFileChanged := err;
  429.         END
  430.     ELSE
  431.         DiskFileChanged := noErr;
  432.     END;
  433.  
  434. {--------------------------------------------------------------------------------------------------}
  435. {$S MAOpen}
  436.  
  437. PROCEDURE TDocument.DoInitialState;
  438. {Called for 'New' & 'Revert' [to blank] commands & for default open tool icon}
  439.  
  440.     BEGIN
  441.     END;
  442.  
  443. {--------------------------------------------------------------------------------------------------}
  444. {$S MAOpen}
  445.  
  446. PROCEDURE TDocument.DoMakeViews(forPrinting: BOOLEAN);
  447.  
  448. { E X A M P L E
  449.     VAR aYOURView: TYOURView;
  450. BEGIN
  451.     NEW(aYOURView);
  452.     aYOURView.IYOURView(SELF, YOURExtentRect);
  453.     DoMakeView := aYOURView;
  454. END;
  455. }
  456.  
  457.     VAR
  458.         aPrintHandler:        TPrintHandler;
  459.         aView:                TView;
  460.  
  461.     BEGIN
  462.     IF qTemplateViews THEN
  463.         BEGIN
  464.         IF forPrinting THEN                                 { Don't need window when Finder printing. }
  465.             aView := DoCreateViews(SELF, NIL, kDefaultViewID, gZeroVPt)
  466.         ELSE
  467.             aView := NewTemplateWindow(kDefaultWindowID, SELF);
  468.         FailNil(aView);
  469.     
  470.         { Install a copy of gPrintHandler into the view.  gPrintHandler will be a real printhandler
  471.         if UPrinting has been initialized otherwise it is a null print handler. }
  472.         aView := aView.FindSubView(kIDDefaultView);
  473.         aPrintHandler := TPrintHandler(gPrintHandler.clone);
  474.         fDocPrintHandler := aPrintHandler;
  475.         aPrintHandler.fDocument := SELF;
  476.         aPrintHandler.fView := aView;
  477.         aPrintHandler.SetDefaultPrintInfo;
  478.         aView.AttachPrintHandler(aPrintHandler);
  479.         END;
  480.     END;
  481.  
  482. {--------------------------------------------------------------------------------------------------}
  483. {$S MADocumentRes}
  484.  
  485. PROCEDURE TDocument.DoMakeWindows;
  486.  
  487.     BEGIN
  488.     END;
  489.  
  490. {--------------------------------------------------------------------------------------------------}
  491. {$S MASelCommand}
  492.  
  493. FUNCTION TDocument.DoMenuCommand(aCmdNumber: CmdNumber): TCommand;
  494.  
  495.     VAR
  496.         aSaveDocCommand:    TSaveDocCommand;
  497.         aRevertDocCommand:    TRevertDocCommand;
  498.         oldObjectPerm:        BOOLEAN;
  499.  
  500.     BEGIN
  501.     { ==================================================================================
  502.     Some commands will be returned to perform actions that must _ALWAYS_ be available.
  503.     The allocation cannot be allowed to fail.  So we do a temp allocation which by
  504.     definition cannot be allowed to fail.  This strategy is used wherever we want to use
  505.     command objects but don't want to leave the user twisting in the breeze.
  506.     NOTE: Don't forget to allow for this memory in your mem! resource if you copy this
  507.     style in your own code.
  508.     ================================================================================== }
  509.  
  510.     DoMenuCommand := NIL;
  511.  
  512.     CASE aCmdNumber OF
  513.  
  514.         cPrFileBase..cPrFileMax:
  515.             IF fDocPrintHandler <> NIL THEN
  516.                 DoMenuCommand := fDocPrintHandler.DoMenuCommand(aCmdNumber);
  517.  
  518.         cSave, cSaveAs, cSaveCopy:
  519.             BEGIN
  520.             oldObjectPerm := AllocateObjectsFromPerm(FALSE);
  521.             New(aSaveDocCommand);
  522.             IF AllocateObjectsFromPerm(oldObjectPerm) THEN;
  523.  
  524.             FailNil(aSaveDocCommand);                    { just in case }
  525.             aSaveDocCommand.ISaveDocCommand(aCmdNumber, SELF);
  526.             DoMenuCommand := aSaveDocCommand;
  527.             END;
  528.  
  529.         cRevert:
  530.             BEGIN
  531.             oldObjectPerm := AllocateObjectsFromPerm(FALSE);
  532.             New(aRevertDocCommand);
  533.             IF AllocateObjectsFromPerm(oldObjectPerm) THEN;
  534.  
  535.             FailNil(aRevertDocCommand);                    { just in case }
  536.             aRevertDocCommand.IRevertDocCommand(aCmdNumber, SELF);
  537.             DoMenuCommand := aRevertDocCommand;
  538.             END;
  539.  
  540.         OTHERWISE
  541.             DoMenuCommand := INHERITED DoMenuCommand(aCmdNumber);
  542.     END;                                                {CASE}
  543.     END;
  544.  
  545. {--------------------------------------------------------------------------------------------------}
  546. {$S MAWriteFile}
  547.  
  548. PROCEDURE TDocument.DoNeedDiskSpace(VAR dataForkBytes, rsrcForkBytes: LONGINT);
  549.  
  550.     BEGIN
  551.     IF fSavePrintInfo THEN
  552.         dataForkBytes := dataForkBytes + kPrintInfoSize;
  553.     IF fUsesRsrcFork THEN
  554.         rsrcForkBytes := rsrcForkBytes + kRsrcFileOverhead;
  555.     END;
  556.  
  557. {--------------------------------------------------------------------------------------------------}
  558. {$S MAReadFile}
  559.  
  560. PROCEDURE TDocument.DoRead(aRefNum: INTEGER;
  561.                            rsrcExists, forPrinting: BOOLEAN);
  562.  
  563.     VAR
  564.         count:                LONGINT;
  565.  
  566.     BEGIN
  567.     IF fSavePrintInfo THEN
  568.         BEGIN
  569.         IF fPrintInfo = NIL THEN
  570.             BEGIN
  571.             fPrintInfo := NewPermHandle(kPrintInfoSize);
  572.             FailNil(fPrintInfo);
  573.             END;
  574.  
  575.         count := kPrintInfoSize;
  576.         FailOSerr(FSRead(aRefNum, count, fPrintInfo^));
  577.         END;
  578.     END;
  579.  
  580. {--------------------------------------------------------------------------------------------------}
  581. {$S MADocumentRes}
  582.  
  583. PROCEDURE TDocument.DoSetupMenus;
  584.  
  585.     BEGIN
  586.     INHERITED DoSetupMenus;
  587.  
  588.     Enable(cSaveAs, TRUE);
  589.     Enable(cSaveCopy, TRUE);
  590.     IF GetChangeCount <> 0 THEN
  591.         BEGIN
  592.         Enable(cSave, TRUE);
  593.         Enable(cRevert, TRUE);
  594.         END;
  595.  
  596.     IF (fDocPrintHandler <> NIL) & (NOT gTarget.HandlesPrintingCommands) THEN
  597.         fDocPrintHandler.DoSetupMenus;
  598.     END;
  599.  
  600. {--------------------------------------------------------------------------------------------------}
  601. {$S MAWriteFile}
  602.  
  603. PROCEDURE TDocument.DoWrite(aRefNum: INTEGER;
  604.                             makingCopy: BOOLEAN);
  605.  
  606.     VAR
  607.         count:                LONGINT;
  608.  
  609.     BEGIN
  610.     IF fSavePrintInfo THEN
  611.         BEGIN
  612.         IF fPrintInfo = NIL THEN
  613.             BEGIN
  614.             {$IFC qDebug}
  615.             ProgramBreak('no print record in document') {???}
  616.             {$ENDC}
  617.             END
  618.         ELSE
  619.             BEGIN
  620.             count := kPrintInfoSize;
  621.             FailOSerr(FSWrite(aRefNum, count, fPrintInfo^));
  622.             END;
  623.         END;
  624.     END;
  625.  
  626. {--------------------------------------------------------------------------------------------------}
  627. {$S MADocumentRes}
  628.  
  629. PROCEDURE TDocument.ForAllViewsDo(PROCEDURE DoToView(aView: TView));
  630.  
  631.     BEGIN
  632.     IF (fViewList <> NIL) THEN
  633.         fViewList.Each(DoToView);
  634.     END;
  635.  
  636. {--------------------------------------------------------------------------------------------------}
  637. {$S MADocumentRes}
  638.  
  639. PROCEDURE TDocument.ForAllWindowsDo(PROCEDURE DoToWind(aWindow: TWindow));
  640.  
  641.     BEGIN
  642.     IF (fWindowList <> NIL) THEN
  643.         fWindowList.Each(DoToWind);
  644.     END;
  645.  
  646. {--------------------------------------------------------------------------------------------------}
  647. {$S MADocumentRes}
  648.  
  649. PROCEDURE TDocument.FreeData;
  650.  
  651.     BEGIN
  652.     END;
  653.  
  654. {--------------------------------------------------------------------------------------------------}
  655. {$S MADocumentRes}
  656.  
  657. PROCEDURE TDocument.FreeFile;
  658.  
  659.     VAR
  660.         err:                OSErr;
  661.  
  662.     BEGIN
  663.     IF fDataOpen | fRsrcOpen THEN
  664.         BEGIN
  665.         err := CloseFile(fDataRefnum, fRsrcRefnum);
  666.         {$IFC qDebug}
  667.         IF err <> noErr THEN
  668.             Writeln('In TDocument.FreeFile: error from CloseFile = ', err: 1);
  669.         {$ENDC}
  670.         END;
  671.     END;
  672.  
  673. {--------------------------------------------------------------------------------------------------}
  674. {$S MAClipboard}
  675.  
  676. PROCEDURE TDocument.FreeFromClipboard;
  677.  
  678.     BEGIN
  679.     DeleteWindow(gClipWindow);
  680.  
  681.     Free;
  682.     END;
  683.  
  684. {--------------------------------------------------------------------------------------------------}
  685. {$S MADocumentRes}
  686.  
  687. FUNCTION TDocument.GetChangeCount: LONGINT;
  688.  
  689.     BEGIN
  690.     GetChangeCount := fChangeCount;
  691.     END;
  692.  
  693.  
  694. {--------------------------------------------------------------------------------------------------}
  695. {$S MAInspector}
  696.  
  697. PROCEDURE TDocument.GetInspectorName(VAR inspectorName: Str255); OVERRIDE;
  698.  
  699.     BEGIN
  700.     inspectorName := fTitle^^;
  701.     END;
  702.  
  703. {--------------------------------------------------------------------------------------------------}
  704. {$S MAWriteFile}
  705.  
  706. FUNCTION TDocument.GetSaveInfo(itsCmdNumber: CmdNumber;
  707.                                copyFInfo: BOOLEAN;
  708.                                VAR cInfo: CInfoPBRec): BOOLEAN;
  709.  
  710.     VAR
  711.         currName:            Str255;
  712.         err:                OSErr;
  713.  
  714.     BEGIN
  715.     IF fSaveExists & copyFInfo THEN
  716.         BEGIN
  717.         currName := fTitle^^;
  718.         WITH cInfo DO
  719.             BEGIN
  720.             ioNamePtr := @currName;
  721.             ioVRefnum := fVolRefNum;
  722.             ioFVersNum := 0;
  723.             ioFDirIndex := 0;
  724.             ioDirID := 0;
  725.             END;
  726.         IF qNeedsROM128K | gConfiguration.hasHFS THEN
  727.             BEGIN
  728.             err := FillInDirID(@cInfo);
  729.             IF err = noErr THEN
  730.                 err := PBGetCatInfo(@cInfo, FALSE);
  731.             END
  732.         ELSE
  733.             err := PBGetFInfo(@cInfo, FALSE);
  734.  
  735.         cInfo.ioNamePtr := NIL;                         {since ptr is invalid when we exit}
  736.  
  737.             {set the type and creator in case it has changed;
  738.                 the file might be on a file server and someone else
  739.                 could have changed the document}
  740.         cInfo.ioFlFndrInfo.fdCreator := fCreator;
  741.         cInfo.ioFlFndrInfo.fdType := fFileType;
  742.         END
  743.     ELSE
  744.         err := fnfErr;                                    {fake error}
  745.  
  746.     IF err = noErr THEN
  747.         GetSaveInfo := TRUE
  748.     ELSE
  749.         BEGIN
  750.         WITH cInfo.ioFlFndrInfo DO
  751.             BEGIN
  752.             fdCreator := fCreator;
  753.             fdType := fFileType;
  754.             END;
  755.  
  756.         GetSaveInfo := FALSE
  757.         END;
  758.     END;
  759.  
  760. {--------------------------------------------------------------------------------------------------}
  761. {$S MAFile}
  762.  
  763. PROCEDURE TDocument.GetTempName(VAR filename: Str255);
  764.  
  765.     CONST
  766.         maxName             = 31;                        {maximum name size to generate}
  767.         maxNumber            = 10;                        {maximum # digits of the random number}
  768.         maxPrefix            = maxName - maxNumber;
  769.  
  770.     VAR
  771.         s:                    Str255;
  772.         apRefnum:            INTEGER;
  773.         apParam:            Handle;
  774.         time:                LONGINT;
  775.  
  776.     BEGIN
  777.     {If the document is untitled, use the application name.}
  778.     IF fTitle^^ = '' THEN
  779.         GetAppParms(filename, apRefnum, apParam)
  780.     ELSE
  781.         filename := fTitle^^;
  782.  
  783.     IF Length(filename) > maxPrefix THEN
  784.         filename := Copy(filename, 1, maxPrefix);
  785.  
  786.     {append a pseudo-random number}
  787.     GetDateTime(time);
  788.     NumToString(ABS(BXOR(time, BRotR(TickCount, 16))), s);
  789.     filename := CONCAT(filename, s);
  790.     END;
  791.  
  792. {--------------------------------------------------------------------------------------------------}
  793. {$S MADocumentRes}
  794.  
  795. FUNCTION TDocument.HandlesPrintingCommands: BOOLEAN; OVERRIDE;
  796.  
  797.     BEGIN
  798.     HandlesPrintingCommands := FALSE;
  799.     END;
  800.  
  801. {--------------------------------------------------------------------------------------------------}
  802. {$S MAWriteFile}
  803.  
  804. PROCEDURE TDocument.MakeNewCopy(makingCopy: BOOLEAN;
  805.                                 validFInfo: BOOLEAN;
  806.                                 VAR cInfo: CInfoPBRec);
  807.  
  808.      {This routine changes the following fields of cInfo:
  809.  
  810.       The ioNamePtr, and ioVRefnum fields must be set to indicate
  811.         the desired file; this routine sets ioDirID to 0.
  812.  
  813.      IF setFInfo is TRUE then all the fields of cInfo
  814.         should be set up.  Otherwise only the file type and creator
  815.         need to be set up.}
  816.  
  817.     VAR
  818.         err:                OSErr;
  819.         dataRefnum:         INTEGER;
  820.         rsrcRefnum:         INTEGER;
  821.         oldVRefnum:         INTEGER;
  822.         fi:                 FailInfo;
  823.  
  824.     PROCEDURE HdlMkNewCopy(error: OSErr;
  825.                            message: LONGINT);
  826.  
  827.         VAR
  828.             err:                OSErr;
  829.  
  830.         BEGIN
  831.         err := CloseFile(dataRefnum, rsrcRefnum);
  832.         {$IFC qDebug}
  833.         IF err <> noErr THEN
  834.             Writeln('In HdlMkNewCopy: error from CloseFile is ', err: 1);
  835.         {$ENDC}
  836.  
  837.         err := DeleteFile(cInfo.ioNamePtr, cInfo.ioVRefnum);
  838.         {$IFC qDebug}
  839.         IF (err <> noErr) & (err <> fnfErr) THEN
  840.             Writeln('In HdlMkNewCopy: error from DeleteFile is ', err: 1);
  841.         {$ENDC}
  842.         END;
  843.  
  844.     BEGIN
  845.     IF fUsesDataFork | fUsesRsrcFork THEN
  846.         BEGIN
  847.         cInfo.ioDirID := 0;
  848.  
  849.         {Initalize these in case we fail before the call to OpenAFile.}
  850.         dataRefnum := kNoFileRefnum;
  851.         rsrcRefnum := kNoFileRefnum;
  852.  
  853.             {Create the file with the desired creator/type,
  854.                 in case we are not going to set the file info
  855.                 below.}
  856.         FailOSerr(Create(cInfo.ioNamePtr^, cInfo.ioVRefnum, cInfo.ioFlFndrInfo.fdCreator,
  857.                          cInfo.ioFlFndrInfo.fdType));
  858.  
  859.         CatchFailures(fi, HdlMkNewCopy);
  860.  
  861.         IF fUsesRsrcFork THEN
  862.             BEGIN
  863.             FailOSerr(GetVol(NIL, oldVRefnum));
  864.             FailOSerr(SetVol(NIL, cInfo.ioVRefnum));
  865.  
  866.             CreateResFile(cInfo.ioNamePtr^);
  867.  
  868.                 {Do this here to ensure that the current volume is
  869.                     reset, even if we fail.}
  870.             FailOSerr(SetVol(NIL, oldVRefnum));
  871.  
  872.             FailResError;                                {this checks the call to CreateResFile}
  873.             END;
  874.  
  875.         IF validFInfo & (NOT makingCopy) THEN
  876.             BEGIN
  877.             IF qNeedsROM128K | gConfiguration.hasHFS THEN
  878.                 FailOSerr(PBSetCatInfo(@cInfo, FALSE))    {??? Poor Man's Search Path ???}
  879.             ELSE
  880.                 BEGIN
  881.                         {NOTE: We can use the cInfo for this call since the
  882.                             fields required by PBSetInfo are a subset of
  883.                             those in the CInfoPBRec; except we set the
  884.                             ioFVersNum field (just in case)}
  885.                 cInfo.ioFVersNum := 0;
  886.                 FailOSerr(PBSetFInfo(@cInfo, FALSE));
  887.                 END;
  888.             END;
  889.  
  890.         FailOSerr(OpenAFile(cInfo.ioNamePtr^, cInfo.ioVRefnum, fUsesDataFork, fUsesRsrcFork,
  891.                             fsRdWrPerm, fsRdWrPerm, dataRefnum, rsrcRefnum));
  892.  
  893.         DoWrite(dataRefnum, makingCopy);
  894.  
  895.         Success(fi);
  896.  
  897.         FailOSerr(CloseFile(dataRefnum, rsrcRefnum));
  898.         END;
  899.     END;
  900.  
  901. {--------------------------------------------------------------------------------------------------}
  902. {$S MAFile}
  903.  
  904. FUNCTION TDocument.OpenAFile(name: Str255;
  905.                              volRefnum: INTEGER;
  906.                              openData, openRsrc: BOOLEAN;
  907.                              dataPerm, rsrcPerm: INTEGER;
  908.                              VAR dataRefnum, rsrcRefnum: INTEGER): OSErr;
  909.  
  910.     BEGIN
  911.     OpenAFile := MAOpenFile(name, volRefnum, openData, openRsrc, dataPerm, rsrcPerm, dataRefnum,
  912.                             rsrcRefnum);
  913.     END;
  914.  
  915. {--------------------------------------------------------------------------------------------------}
  916. {$S MAOpen}
  917.  
  918. PROCEDURE TDocument.OpenAgain(itsCmdNumber: CmdNumber;
  919.                               openingDoc: TDocument);
  920.  
  921.     VAR
  922.         window:             TWindow;
  923.         s:                    Str255;
  924.  
  925.     BEGIN
  926.     s := fTitle^^;                                        { because ParamText allocates memory }
  927.     ParamText(s, '', '', '');
  928.  
  929.     IF fReopenAlert THEN
  930.         StdAlert(phReopenDoc);                            {!!! This should be programatically
  931.                                                          defeatable }
  932.  
  933.     IF (fWindowList <> NIL) THEN
  934.         BEGIN
  935.         window := TWindow(fWindowList.First);            {??? this seems funky }
  936.         window.Select;
  937.         END;
  938.  
  939.     Failure(0, 0);
  940.     END;
  941.  
  942. {--------------------------------------------------------------------------------------------------}
  943. {$S MAClose}
  944.  
  945. FUNCTION TDocument.PoseSaveDialog: INTEGER;
  946.  
  947.     VAR
  948.         idx:                INTEGER;
  949.         name:                Str255;
  950.         reason:             Str255;
  951.  
  952.     BEGIN
  953.     IF GetChangeCount <> 0 THEN
  954.         BEGIN
  955.         IF gAppDone THEN
  956.             idx := bzQuitting
  957.         ELSE
  958.             idx := bzClosing;
  959.  
  960.         GetIndString(reason, kIDBuzzString, idx);
  961.         name := fTitle^^;                                { ParamText can compact heap }
  962.         ParamText(name, reason, '', '');
  963.  
  964.         PoseSaveDialog := MacAppAlert(phSaveChanges, NIL); {!!! This should be programatically
  965.                                                             defeatable }
  966.         END
  967.     ELSE
  968.         PoseSaveDialog := kNoButton;
  969.     END;
  970.  
  971. {--------------------------------------------------------------------------------------------------}
  972. {$S MAReadFile}
  973.  
  974. PROCEDURE TDocument.ReadFromFile(VAR anAppFile: AppFile;
  975.                                  forPrinting: BOOLEAN);
  976.  
  977.     VAR
  978.         fi:                 FailInfo;
  979.         dataRefnum:         INTEGER;
  980.         rsrcRefnum:         INTEGER;
  981.         isRevert:            BOOLEAN;
  982.         shouldOpenData:     BOOLEAN;
  983.         shouldOpenRsrc:     BOOLEAN;
  984.  
  985.     PROCEDURE HdlRead(error: INTEGER;
  986.                       message: LONGINT);
  987.  
  988.         VAR
  989.             err:                INTEGER;
  990.  
  991.         BEGIN
  992.         err := CloseFile(dataRefnum, rsrcRefnum);
  993.         {$IFC qDebug}
  994.         IF err <> noErr THEN
  995.             Writeln('In HdlOpen: error from CloseFile is ', err: 1);
  996.         {$ENDC}
  997.  
  998.         {caller should set the message as appropriate}
  999.         END;
  1000.  
  1001.     BEGIN
  1002.     isRevert := (anAppFile.fName = '');                 {signal to re-read file}
  1003.     IF isRevert THEN
  1004.         BEGIN
  1005.         anAppFile.fName := fTitle^^;
  1006.         anAppFile.vRefnum := fVolRefNum;
  1007.         END
  1008.     ELSE
  1009.         BEGIN
  1010.         SetString(fTitle, anAppFile.fName);
  1011.         IF fTitle^^ <> anAppFile.fName THEN             { ??? how to test if SetString worked ??? }
  1012.             FailOSerr(memFullErr);
  1013.         fVolRefNum := anAppFile.vRefnum;
  1014.         END;
  1015.  
  1016.     {Don't attempt to open the fork(s) again if they're already open.
  1017.         We do all this rather than close and reopen so that we need not
  1018.         search the directory again, an expensive operation on large MFS
  1019.         disks.}
  1020.     shouldOpenData := fUsesDataFork & NOT (fDataOpen & isRevert);
  1021.     shouldOpenRsrc := fUsesRsrcFork & NOT (fRsrcOpen & isRevert);
  1022.  
  1023.     {Make sure CloseFile operates properly if OpenAFile fails.}
  1024.     dataRefnum := kNoFileRefnum;
  1025.     rsrcRefnum := kNoFileRefnum;
  1026.  
  1027.     CatchFailures(fi, HdlRead);
  1028.  
  1029.     FailOSerr(OpenAFile(anAppFile.fName, anAppFile.vRefnum, shouldOpenData, shouldOpenRsrc,
  1030.                         fDataPerm, fRsrcPerm, dataRefnum, rsrcRefnum));
  1031.  
  1032.     fSaveExists := TRUE;
  1033.  
  1034.     {If the file is already open, use the refnums we already have.
  1035.         Make sure that the document's data fork is positioned at TOF.
  1036.         Make sure that the document's resource file is on top.}
  1037.     IF fDataOpen & NOT shouldOpenData THEN
  1038.         BEGIN
  1039.         dataRefnum := fDataRefnum;
  1040.         FailOSerr(SetFPos(dataRefnum, fsFromStart, 0));
  1041.         END;
  1042.     IF fRsrcOpen & NOT shouldOpenRsrc THEN
  1043.         BEGIN
  1044.         rsrcRefnum := fRsrcRefnum;
  1045.         UseResFile(rsrcRefnum);
  1046.         END;
  1047.  
  1048.     DoRead(dataRefnum,                                    {rsrcExists:} rsrcRefnum <> kNoFileRefnum,
  1049.            forPrinting);
  1050.  
  1051.     SetChangeCount(0);
  1052.  
  1053.     Success(fi);
  1054.  
  1055.     IF fDataOpen THEN
  1056.         BEGIN
  1057.         fDataRefnum := dataRefnum;                        {save valid refnum}
  1058.         dataRefnum := kNoFileRefnum;                    {make sure CloseFile doesn't close it}
  1059.         END
  1060.     ELSE
  1061.         fDataRefnum := kNoFileRefnum;                    {save the no file refnum}
  1062.  
  1063.     IF fRsrcOpen THEN
  1064.         BEGIN
  1065.         fRsrcRefnum := rsrcRefnum;                        {save valid refnum}
  1066.         UseResFile(fRsrcRefnum);                        {make sure it is the current map}
  1067.         rsrcRefnum := kNoFileRefnum;                    {make sure CloseFile doesn't close it}
  1068.         END
  1069.     ELSE
  1070.         fRsrcRefnum := kNoFileRefnum;                    {save the no file refnum}
  1071.  
  1072.     FailOSerr(CloseFile(dataRefnum, rsrcRefnum));
  1073.  
  1074.     fModDate := FileModDate(anAppFile.fName, anAppFile.vRefnum);
  1075.     END;
  1076.  
  1077. {--------------------------------------------------------------------------------------------------}
  1078. {$S MAWriteFile}
  1079.  
  1080. PROCEDURE TDocument.RequestFileName(itsCmdNumber: CmdNumber;
  1081.                                     makingCopy: BOOLEAN;
  1082.                                     VAR filename: Str255;
  1083.                                     VAR volRefnum: INTEGER);
  1084.  
  1085.     VAR
  1086.         reply:                SFReply;
  1087.         dlgID:                INTEGER;
  1088.         prompt:             Str255;
  1089.         dlgLoc:             Point;
  1090.         dlgHook:            ProcPtr;
  1091.         filterProc:         ProcPtr;
  1092.         otherDoc:            TDocument;
  1093.         err:                OSErr;
  1094.  
  1095.     BEGIN
  1096.     filename := fTitle^^;
  1097.     SFPutParms(itsCmdNumber, dlgID, dlgLoc, filename, prompt, dlgHook, filterProc);
  1098.  
  1099.     {$IFC qDebug}
  1100.     gRsrcCheck := 0;                                    {force immediate check}
  1101.     {$ENDC}
  1102.  
  1103.     {Update all the windows to avoid a bug in Standard File in which
  1104.         you can't mount a disk correctly when window updates are pending.}
  1105.     gApplication.UpdateAllWindows;
  1106.  
  1107.     SFPPutFile(dlgLoc, prompt, filename, dlgHook, reply, dlgID, filterProc);
  1108.  
  1109.     IF reply.good THEN
  1110.         BEGIN
  1111.         filename := reply.fName;
  1112.         volRefnum := reply.vRefnum;
  1113.  
  1114.         {See if there is an open document with the same name.  If there
  1115.             is, tell it we're trying to save it again, which will
  1116.             ordinarily result in failure.}
  1117.         otherDoc := gApplication.AlreadyOpen(filename, volRefnum);
  1118.         IF otherDoc <> NIL THEN
  1119.             otherDoc.SaveAgain(itsCmdNumber, makingCopy, SELF);
  1120.  
  1121.         {User has already confirmed deleting target in this case,
  1122.             so trash file and get maximum disk space.}
  1123.         err := DeleteFile(@filename, volRefnum);
  1124.         IF (err <> noErr) & (err <> fnfErr) THEN
  1125.             Failure(err, 0);
  1126.         END
  1127.     ELSE
  1128.         Failure(noErr, msgCancelled);                    {user cancelled}
  1129.     END;
  1130.  
  1131. {--------------------------------------------------------------------------------------------------}
  1132. {$S MAReadFile}
  1133.  
  1134. PROCEDURE TDocument.Revert;
  1135.  
  1136.     VAR
  1137.         anAppFile:            AppFile;
  1138.         fi:                 FailInfo;
  1139.         lastCommand:        TCommand;
  1140.  
  1141.     PROCEDURE HdlRevert(error: OSErr;
  1142.                         message: LONGINT);
  1143.  
  1144.         BEGIN
  1145.         IF error = fnfErr THEN
  1146.             error := errRevertFNF;
  1147.         IF message = 0 THEN
  1148.             gErrorParm3 := fTitle^^;
  1149.         FailNewMessage(error, message, msgRevertFailed);
  1150.         END;
  1151.  
  1152.     PROCEDURE ResetPrintHandler(view: TView);
  1153.  
  1154.         BEGIN
  1155.         IF view.fPrintHandler <> NIL THEN
  1156.             view.fPrintHandler.Reset;
  1157.         END;
  1158.  
  1159.     BEGIN
  1160.     CatchFailures(fi, HdlRevert);
  1161.  
  1162.     CheckDiskFile(kIDBuzzString, bzRevertAnyways,        {reverting:} TRUE);
  1163.  
  1164.     lastCommand := GetLastCommand;
  1165.     IF (lastCommand <> NIL) & (lastCommand.fChangedDocument = SELF) THEN
  1166.         CommitLastCommand;
  1167.  
  1168.     FreeData;
  1169.  
  1170.     IF fSaveExists THEN
  1171.         BEGIN
  1172.         anAppFile.fName := '';                            {signal that we are reverting}
  1173.         ReadFromFile(anAppFile, kForDisplay);
  1174.         END
  1175.     ELSE
  1176.         BEGIN
  1177.         IF (fViewList <> NIL) THEN
  1178.             fViewList.Each(ResetPrintHandler);
  1179.         DoInitialState;
  1180.         END;
  1181.  
  1182.     SetChangeCount(0);
  1183.  
  1184.     Success(fi);
  1185.     END;
  1186.  
  1187.     {Notes:
  1188.         Parameter combinations:
  1189.             ask?    copy?
  1190.              T          F     Save As... or Save of Untitled doc
  1191.              T          T     Save a Copy In...
  1192.              F          F     Save of a titled doc
  1193.  
  1194.         Caller is responsible for passing askForFilename = TRUE
  1195.             if the document is currently untitled.
  1196.  
  1197.         If askForFilename is FALSE, we force switchToTarget to TRUE.
  1198.  
  1199.         We call SaveInPlace only if there is insufficient disk space
  1200.             to save a temporary copy and we are allowed to save in
  1201.             place (according to the fSaveInPlace field).
  1202.  
  1203.         If askForFilename is TRUE, SaveInPlace should not be called
  1204.             since we delete the target immediately.  (If there is not
  1205.             enough room to save via a temporary file, we would not
  1206.             be able to save in place either.)
  1207.     }
  1208.  
  1209. {--------------------------------------------------------------------------------------------------}
  1210. {$S MAReadFile}
  1211.  
  1212. PROCEDURE TDocument.Abandon;
  1213.  
  1214.     BEGIN
  1215.     { If your document needs to do some cleanup when its being abandoned then
  1216.     put some code in an override of this method }
  1217.     END;
  1218.  
  1219. {--------------------------------------------------------------------------------------------------}
  1220. {$S MAWriteFile}
  1221.  
  1222. PROCEDURE TDocument.Save(itsCmdNumber: CmdNumber;
  1223.                          askForFilename, makingCopy: BOOLEAN);
  1224.  
  1225.     VAR
  1226.         name:                Str255;
  1227.         volRefnum:            INTEGER;
  1228.  
  1229.         hPB:                HParamBlockRec;
  1230.  
  1231.         dataBytes:            LONGINT;
  1232.         rsrcBytes:            LONGINT;
  1233.         neededBlks:         LONGINT;
  1234.         usedBlks:            LONGINT;
  1235.         freeBlks:            LONGINT;
  1236.         blkSize:            LONGINT;
  1237.  
  1238.         copyFInfo:            BOOLEAN;
  1239.         canSaveInPlace:     BOOLEAN;
  1240.         oldFlag:            BOOLEAN;
  1241.         fi:                 FailInfo;
  1242.         err:                OSErr;
  1243.  
  1244.         otherDoc:            TDocument;
  1245.         lastCommand:        TCommand;
  1246.  
  1247.     PROCEDURE HdlSave(error: INTEGER;
  1248.                       message: LONGINT);
  1249.  
  1250.         VAR
  1251.             newMsg:             LONGINT;
  1252.  
  1253.         BEGIN
  1254.         err := FlushVol(NIL, volRefnum);
  1255.  
  1256.         IF message = 0 THEN
  1257.             gErrorParm3 := name;
  1258.  
  1259.         IF NOT askForFilename THEN
  1260.             newMsg := msgSaveFailed
  1261.         ELSE IF makingCopy THEN
  1262.             newMsg := msgSaveCopyFailed
  1263.         ELSE
  1264.             newMsg := msgSaveAsFailed;
  1265.  
  1266.         FailNewMessage(error, message, newMsg);
  1267.         END;
  1268.  
  1269.     BEGIN
  1270.     CatchFailures(fi, HdlSave);
  1271.  
  1272.     {Step 1: Get the target of the save}
  1273.     {Caller should set askForFilename if this is an Untitled document}
  1274.     IF askForFilename THEN
  1275.         RequestFileName(itsCmdNumber, makingCopy, name, volRefnum)
  1276.     ELSE
  1277.         BEGIN
  1278.         name := fTitle^^;
  1279.         volRefnum := fVolRefNum;
  1280.         END;
  1281.  
  1282. {Step 2: Decide whether to save with a temporary file or in place,
  1283.         and call appropriate method (SaveViaTemp or SaveInPlace).}
  1284.  
  1285.     copyFInfo := NOT (askForFilename | makingCopy);
  1286.  
  1287.     IF copyFInfo THEN
  1288.         CheckDiskFile(kIDBuzzString, bzSaveAnyways,     {reverting:} FALSE);
  1289.  
  1290.     AboutToSave(itsCmdNumber, name, volRefnum, makingCopy);
  1291.  
  1292.     lastCommand := GetLastCommand;
  1293.     IF fCommitOnSave | (NOT makingCopy) & (lastCommand <> NIL) & (lastCommand.fChangedDocument =
  1294.        SELF) THEN
  1295.         CommitLastCommand;
  1296.  
  1297.     {Get information about the volume saving to}
  1298.     WITH hPB DO
  1299.         BEGIN
  1300.         ioNamePtr := NIL;
  1301.         ioVRefnum := volRefnum;
  1302.         ioVolIndex := 0;
  1303.         END;
  1304.     FailOSerr(PBHGetVInfo(@hPB, FALSE));
  1305.  
  1306.         {on HFS ioVFrBlk is an unsigned INTEGER; on MFS it is
  1307.             limited to a positive signed INTEGER}
  1308.     freeBlks := BAND(hPB.ioVFrBlk, $0000FFFF) - 1;        {-1 for some slop -- don't try to fill up
  1309.                                                          the disk completely}
  1310.  
  1311.     {compute size needed to save document}
  1312.     blkSize := hPB.ioVAlBlkSiz;
  1313.  
  1314.     dataBytes := 0;
  1315.     rsrcBytes := 0;
  1316.     DoNeedDiskSpace(dataBytes, rsrcBytes);
  1317.     neededBlks := NumBlocks(rsrcBytes, blkSize) + NumBlocks(dataBytes, blkSize);
  1318.  
  1319.     IF freeBlks >= neededBlks THEN
  1320.     {enough disk space to create a second copy of document}
  1321.         SaveViaTemp(itsCmdNumber, makingCopy, copyFInfo, name, volRefnum)
  1322.     ELSE
  1323.         BEGIN                                            {cannot make a duplicate of document}
  1324.         {Check to see if we can save the file in place.}
  1325.  
  1326.         canSaveInPlace := FALSE;                        {default value}
  1327.  
  1328.         IF fSaveInPlace <> sipNever THEN
  1329.             BEGIN
  1330.             {See if target exists, if the disk space it uses is
  1331.                 enough to allow us to save the file after deleting it.}
  1332.             err := GetFileInfo(name, volRefnum, hPB);
  1333.  
  1334.             IF err = noErr THEN
  1335.                 BEGIN
  1336.                 {compute # block used by target}
  1337.                 usedBlks := NumBlocks(hPB.ioFlRPyLen, blkSize) + NumBlocks(hPB.ioFlPyLen, blkSize);
  1338.  
  1339.                 IF neededBlks <= usedBlks + freeBlks THEN
  1340.                 {we could save if target is deleted first}
  1341.                     BEGIN
  1342.                     IF fSaveInPlace = sipAskUser THEN
  1343.                         BEGIN
  1344.                         ParamText(name, '', '', '');
  1345.                         IF MacAppAlert(phPurgeOld, NIL) = kYesButton THEN {!!! This should be
  1346.                                                                            programatically
  1347.                                                                            defeatable }
  1348.                             canSaveInPlace := TRUE
  1349.                         ELSE
  1350.                             Failure(noErr, msgCancelled);
  1351.                         END
  1352.                     ELSE                                {we know fSaveInPlace <> sipNever; it must
  1353.                                                          be sipAlways}
  1354.                         canSaveInPlace := TRUE;
  1355.                     END;
  1356.                 END
  1357.             ELSE IF err <> fnfErr THEN
  1358.                 Failure(err, 0);
  1359.             END;
  1360.  
  1361.         IF canSaveInPlace THEN
  1362.             SaveInPlace(itsCmdNumber, makingCopy, copyFInfo, name, volRefnum)
  1363.         ELSE
  1364.             Failure(dskFulErr, 0);
  1365.         END;
  1366.  
  1367.     Success(fi);                                        {??? Put later in proc ???}
  1368.  
  1369.     {$IFC qDebug}
  1370.     err := GetFileInfo(name, volRefnum, hPB);
  1371.     IF err = noErr THEN
  1372.         BEGIN
  1373.         usedBlks := NumBlocks(hPB.ioFlRPyLen, blkSize) + NumBlocks(hPB.ioFlPyLen, blkSize);
  1374.         IF usedBlks <> neededBlks THEN
  1375.             BEGIN
  1376.             Writeln('In TDocument.Save: DoNeedDiskSpace estimated disk space incorrectly.');
  1377.             Writeln('estimated # disk blocks = ', neededBlks: 1);
  1378.             Writeln('   actual # disk blocks = ', usedBlks: 1);
  1379.             END;
  1380.         END;
  1381.     {$ENDC}
  1382.  
  1383.     {Step 3: Tell the document that the save was successful.}
  1384.     IF NOT makingCopy THEN
  1385.         SavedOn(name, volRefnum);
  1386.  
  1387.     err := FlushVol(NIL, volRefnum);
  1388.     END;
  1389.  
  1390. {--------------------------------------------------------------------------------------------------}
  1391. {$S MAWriteFile}
  1392.  
  1393. PROCEDURE TDocument.SaveAgain(itsCmdNumber: CmdNumber;
  1394.                               makingCopy: BOOLEAN;
  1395.                               savingDoc: TDocument);
  1396.  
  1397.     BEGIN
  1398.     {Don't save the file if another one of the same name is already open.}
  1399.     IF savingDoc <> SELF THEN
  1400.         Failure(errSaveAgain, 0);
  1401.     END;
  1402.  
  1403. {--------------------------------------------------------------------------------------------------}
  1404. {$S MAWriteFile}
  1405.  
  1406. PROCEDURE TDocument.SavedOn(VAR filename: Str255;
  1407.                             volRefnum: INTEGER);
  1408.  
  1409.     VAR
  1410.         dataRefnum:         INTEGER;
  1411.         rsrcRefnum:         INTEGER;
  1412.  
  1413.     BEGIN
  1414.     SetChangeCount(0);
  1415.     fSaveExists := TRUE;
  1416.  
  1417.     IF fTitle^^ <> filename THEN
  1418.         SetTitle(filename);
  1419.     fVolRefNum := volRefnum;
  1420.  
  1421.     fModDate := FileModDate(filename, volRefnum);
  1422.  
  1423.     FailOSerr(OpenAFile(filename, volRefnum, fDataOpen, fRsrcOpen, fDataPerm, fRsrcPerm, dataRefnum,
  1424.                         rsrcRefnum));
  1425.     fDataRefnum := dataRefnum;
  1426.     fRsrcRefnum := rsrcRefnum;
  1427.     END;
  1428.  
  1429. {--------------------------------------------------------------------------------------------------}
  1430. {$S MAWriteFile}
  1431.  
  1432. PROCEDURE TDocument.SaveInPlace(itsCmdNumber: CmdNumber;
  1433.                                 makingCopy, copyFInfo: BOOLEAN;
  1434.                                 VAR filename: Str255;
  1435.                                 volRefnum: INTEGER);
  1436. {fileName is VAR only to avoid copying}
  1437.  
  1438.     VAR
  1439.         cInfo:                CInfoPBRec;
  1440.         validInfo:            BOOLEAN;
  1441.         err:                OSErr;
  1442.  
  1443.     BEGIN
  1444.     IF NOT (fDataOpen | fRsrcOpen) THEN
  1445.         BEGIN
  1446.         validInfo := GetSaveInfo(itsCmdNumber, copyFInfo, cInfo);
  1447.  
  1448.         {Tell document that the file is going away.}
  1449.         FreeFile;
  1450.  
  1451.         {Delete the current file.}
  1452.         err := DeleteFile(@filename, volRefnum);
  1453.         IF (err <> noErr) & (err <> fnfErr) THEN
  1454.             Failure(err, 0);
  1455.  
  1456.         {Save a new copy.}
  1457.         cInfo.ioNamePtr := @filename;
  1458.         cInfo.ioVRefnum := volRefnum;
  1459.  
  1460.         MakeNewCopy(makingCopy, validInfo, cInfo);
  1461.         END
  1462.     ELSE
  1463.         BEGIN
  1464.         {$IFC qDebug}
  1465.         ProgramBreak('You must override TDocument.SaveInPlace for a disk-based document.');
  1466.         {$ENDC}
  1467.         END;
  1468.     END;
  1469.  
  1470. {--------------------------------------------------------------------------------------------------}
  1471. {$S MAWriteFile}
  1472.  
  1473. PROCEDURE TDocument.SaveViaTemp(itsCmdNumber: CmdNumber;
  1474.                                 makingCopy, copyFInfo: BOOLEAN;
  1475.                                 VAR filename: Str255;
  1476.                                 volRefnum: INTEGER);
  1477. {fileName is VAR only to avoid copying}
  1478.  
  1479.     VAR
  1480.         cInfo:                CInfoPBRec;
  1481.         validInfo:            BOOLEAN;
  1482.         tmpName:            Str255;
  1483.         fi:                 FailInfo;
  1484.         err:                OSErr;
  1485.  
  1486.     PROCEDURE HdlSvTemp(error: OSErr;
  1487.                         message: LONGINT);
  1488.  
  1489.         VAR
  1490.             err:                OSErr;
  1491.  
  1492.         BEGIN
  1493.         err := DeleteFile(@tmpName, volRefnum);
  1494.         {$IFC qDebug}
  1495.         IF (err <> noErr) & (err <> fnfErr) THEN
  1496.             Writeln('In HdlSvTemp: error from DeleteFile is ', err: 1);
  1497.         {$ENDC}
  1498.         END;
  1499.  
  1500.     PROCEDURE HdlSaveFailed(error: OSErr;
  1501.                             message: LONGINT);
  1502.  
  1503.         VAR
  1504.             dataRefnum:         INTEGER;
  1505.             rsrcRefnum:         INTEGER;
  1506.             fi:                 FailInfo;
  1507.  
  1508.             {If reopen attempt fails, make sure original error gets through.}
  1509.  
  1510.         PROCEDURE HdlFailFailed(newError: OSErr;
  1511.                                 newMessage: LONGINT);
  1512.  
  1513.             BEGIN
  1514.             Failure(error, message);
  1515.             END;
  1516.  
  1517.         BEGIN
  1518.         HdlSvTemp(error, message);
  1519.         IF fSaveExists & (NOT makingCopy) THEN
  1520.             BEGIN
  1521.             CatchFailures(fi, HdlFailFailed);
  1522.             LockHandleHigh(Handle(fTitle));
  1523.             FailOSerr(OpenAFile(fTitle^^, fVolRefNum, fDataOpen, fRsrcOpen, fDataPerm, fRsrcPerm,
  1524.                                 dataRefnum, rsrcRefnum));
  1525.             HUnLock(Handle(fTitle));
  1526.             Success(fi);
  1527.             fDataRefnum := dataRefnum;
  1528.             fRsrcRefnum := rsrcRefnum;
  1529.             END;
  1530.         END;
  1531.  
  1532.     BEGIN
  1533.     validInfo := GetSaveInfo(itsCmdNumber, copyFInfo, cInfo);
  1534.  
  1535.     GetTempName(tmpName);
  1536.  
  1537.     cInfo.ioNamePtr := @tmpName;
  1538.     cInfo.ioVRefnum := volRefnum;
  1539.  
  1540.     MakeNewCopy(makingCopy, validInfo, cInfo);
  1541.  
  1542.     CatchFailures(fi, HdlSvTemp);
  1543.  
  1544.     {Tell document that the file is going away}
  1545.     IF NOT makingCopy THEN
  1546.         FreeFile;
  1547.  
  1548.     Success(fi);
  1549.  
  1550.     CatchFailures(fi, HdlSaveFailed);
  1551.  
  1552.     {Delete the old copy if it exists.}
  1553.     err := DeleteFile(@filename, volRefnum);
  1554.     IF (err <> noErr) & (err <> fnfErr) THEN
  1555.         Failure(err, 0);
  1556.  
  1557.     FailOSerr(Rename(tmpName, volRefnum, filename));
  1558.  
  1559.     Success(fi);
  1560.     END;
  1561.  
  1562. {--------------------------------------------------------------------------------------------------}
  1563. {$S MADocumentRes}
  1564.  
  1565. PROCEDURE TDocument.SetTitle(aTitle: Str255);
  1566.  
  1567.     PROCEDURE InstallTitle(aWindow: TWindow);
  1568.  
  1569.         BEGIN
  1570.         aWindow.SetTitleForDoc(aTitle);
  1571.         END;
  1572.  
  1573.     BEGIN
  1574.     SetString(fTitle, aTitle);
  1575.     IF fTitle^^ <> aTitle THEN                            { ??? how to test if SetString worked ??? }
  1576.         FailOSerr(memFullErr);
  1577.     ForAllWindowsDo(InstallTitle);
  1578.     END;
  1579.  
  1580. {--------------------------------------------------------------------------------------------------}
  1581. {$S MADocumentRes}
  1582.  
  1583. PROCEDURE TDocument.SetChangeCount(newChangeCount: LONGINT);
  1584.  
  1585. { (??? should we add this as a default action with a TView.DocumentChanged method?)
  1586. You can notify your views that the document changed something like this:
  1587.     PROCEDURE NotifyChange(aView: TView);
  1588.  
  1589.         BEGIN
  1590.         if Member(aView, TMyClass) THEN
  1591.             TMyView(aView).DocumentChanged(newChangeCount);
  1592.         END;
  1593. }
  1594.  
  1595.     BEGIN
  1596.     fChangeCount := newChangeCount;
  1597.     { ForAllViewsDo(NotifyChange); }
  1598.     END;
  1599.  
  1600. {--------------------------------------------------------------------------------------------------}
  1601. {$S MAWriteFile}
  1602.  
  1603. PROCEDURE TDocument.SFPutParms(itsCmdNumber: CmdNumber;
  1604.                                VAR dlgID: INTEGER;
  1605.                                VAR where: Point;
  1606.                                VAR defaultName, prompt: Str255;
  1607.                                VAR dlgHook, filterProc: ProcPtr);
  1608.  
  1609.     VAR
  1610.         dlogTemplate:        DialogTHndl;
  1611.         dialogRect:         Rect;
  1612.         idx:                INTEGER;
  1613.  
  1614.     BEGIN
  1615.     dlgID := putDlgID;                                    {putDlgID defined by Standard File}
  1616.  
  1617.     { compute the top-left location of the dialog }
  1618.     dlogTemplate := DialogTHndl(GetResource('DLOG', dlgID));
  1619.     IF dlogTemplate <> NIL THEN
  1620.         BEGIN
  1621.         dialogRect := dlogTemplate^^.boundsRect;
  1622.         CenterRectOnScreen(dialogRect, TRUE, TRUE, TRUE);
  1623.         where := dialogRect.topleft;
  1624.         END
  1625.     ELSE
  1626.         SetPt(where, 100, 100);
  1627.  
  1628.     CASE itsCmdNumber OF
  1629.         cSave, cSaveAs:
  1630.             idx := bzSaveAs;
  1631.         cSaveCopy:
  1632.             idx := bzSaveCopy;
  1633.         OTHERWISE
  1634.             idx := 0;
  1635.     END;
  1636.  
  1637.     IF idx = 0 THEN
  1638.         prompt := ''
  1639.     ELSE
  1640.         GetIndString(prompt, kIDBuzzString, idx);
  1641.  
  1642.     dlgHook := NIL;
  1643.     filterProc := NIL;
  1644.     END;
  1645.  
  1646. {--------------------------------------------------------------------------------------------------}
  1647. {$S MAReadFile}
  1648.  
  1649. PROCEDURE TDocument.ShowReverted;
  1650.  
  1651.     PROCEDURE RevertView(aView: TView);
  1652.  
  1653.         BEGIN
  1654.         aView.ShowReverted;
  1655.         END;
  1656.  
  1657.     BEGIN
  1658.     ForAllViewsDo(RevertView);
  1659.     END;
  1660.  
  1661. {--------------------------------------------------------------------------------------------------}
  1662. {$S MAOpen}
  1663.  
  1664. PROCEDURE TDocument.ShowWindows;
  1665.  
  1666.     FUNCTION ShowAWindow(aWindow: TWindow): BOOLEAN;
  1667.  
  1668.         BEGIN
  1669.         IF aWindow.fOpenInitially THEN
  1670.             aWindow.Open;
  1671.         ShowAWindow := FALSE;
  1672.         END;
  1673.  
  1674.     BEGIN
  1675.     { Make the windows open from back to front }
  1676.     IF (fWindowList <> NIL) & (fWindowList.LastThat(ShowAWindow) <> NIL) THEN;
  1677.     END;
  1678.  
  1679. {--------------------------------------------------------------------------------------------------}
  1680. {$S MAOpen}
  1681.  
  1682. PROCEDURE TDocument.UntitledName(VAR noName: Str255);
  1683.  
  1684.     VAR
  1685.         preInsert:            INTEGER;
  1686.         constChars:         INTEGER;
  1687.         num:                Str255;
  1688.  
  1689.     BEGIN
  1690.     GetIndString(noName, kIDBuzzString, bzUntitled);
  1691.     IF ParseTitleTemplate(noName, preInsert, constChars) THEN
  1692.         BEGIN
  1693.         NumToString(gNumUntitled, num);
  1694.  
  1695.         IF SubstituteInTitle(noName, num, preInsert, constChars) THEN
  1696.             gNumUntitled := gNumUntitled + 1;
  1697.         END;
  1698.     END;
  1699.  
  1700. {--------------------------------------------------------------------------------------------------}
  1701. {$S MAFields}
  1702.  
  1703. PROCEDURE TDocument.Fields(PROCEDURE DoToField(fieldName: Str255;
  1704.                                                fieldAddr: Ptr;
  1705.                                                fieldType: INTEGER));
  1706.  
  1707.     BEGIN
  1708.     DoToField('TDocument', NIL, bClass);
  1709.     DoToField('fWindowList', @fWindowList, bObject);
  1710.     DoToField('fViewList', @fViewList, bObject);
  1711.     DoToField('fChangeCount', @fChangeCount, bLongInt);
  1712.     DoToField('fDocPrintHandler', @fDocPrintHandler, bObject);
  1713.     DoToField('fSavePrintInfo', @fSavePrintInfo, bBoolean);
  1714.     DoToField('fSharePrintInfo', @fSharePrintInfo, bBoolean);
  1715.     DoToField('fPrintInfo', @fPrintInfo, bHandle);
  1716.     DoToField('fTitle', @fTitle, bStringHandle);
  1717.     DoToField('fFileType', @fFileType, bOSType);
  1718.     DoToField('fCreator', @fCreator, bOSType);
  1719.     DoToField('fVolRefNum', @fVolRefNum, bInteger);
  1720.     DoToField('fModDate', @fModDate, bLongInt);
  1721.     DoToField('fReopenAlert', @fReopenAlert, bBoolean);
  1722.     DoToField('fSaveExists', @fSaveExists, bBoolean);
  1723.     DoToField('fCommitOnSave', @fCommitOnSave, bBoolean);
  1724.     DoToField('fUsesDataFork', @fUsesDataFork, bBoolean);
  1725.     DoToField('fUsesRsrcFork', @fUsesRsrcFork, bBoolean);
  1726.     DoToField('fDataOpen', @fDataOpen, bBoolean);
  1727.     DoToField('fRsrcOpen', @fRsrcOpen, bBoolean);
  1728.     DoToField('fDataPerm', @fDataPerm, bInteger);
  1729.     DoToField('fRsrcPerm', @fRsrcPerm, bInteger);
  1730.     DoToField('fDataRefnum', @fDataRefnum, bInteger);
  1731.     DoToField('fRsrcRefNum', @fRsrcRefnum, bInteger);
  1732.     DoToField('fSaveInPlace', @fSaveInPlace, bByte);
  1733.     INHERITED Fields(DoToField);
  1734.     END;
  1735.